import { JsonValueType } from "./type";

/**
 * Convert camelCase to PascalCase
 * @param str Input string
 * @returns String in PascalCase format
 */
export function toPascalCase(str: string): string {
  // Handle special characters and invalid naming
  const validStr = str
    .replace(/[^\w\s\-_]/g, "") // Remove non-alphanumeric, non-underscore characters
    .replace(/^\d+/, ""); // Remove leading digits

  // If the result is empty, return a default name
  if (!validStr) return "Item";

  return validStr
    .split(/[\s\-_]+/) // Split by spaces, hyphens, and underscores
    .map(part => part.charAt(0).toUpperCase() + part.slice(1))
    .join("");
}

/**
 * Generate type name
 * @param baseName Base name
 * @param type Type
 * @returns Formatted type name
 */
export function generateTypeName(
  baseName: string,
  type: JsonValueType
): string {
  const pascalName = toPascalCase(baseName);

  switch (type) {
    case JsonValueType.ARRAY:
      return `${pascalName}Array`;
    case JsonValueType.OBJECT:
      return `${pascalName}Object`;
    default:
      return pascalName;
  }
}

/**
 * Generate root type name
 * @param type Root type of JSON
 * @param rootName Optional root name prefix, defaults to 'Root'
 * @returns Root type name
 */
export function generateRootTypeName(
  type: JsonValueType,
  rootName: string = "Root"
): string {
  switch (type) {
    case JsonValueType.ARRAY:
      return `${rootName}Array`;
    case JsonValueType.OBJECT:
      return `${rootName}Object`;
    case JsonValueType.STRING:
      return `${rootName}String`;
    case JsonValueType.NUMBER:
      return `${rootName}Number`;
    case JsonValueType.BOOLEAN:
      return `${rootName}Boolean`;
    case JsonValueType.NULL:
      return `${rootName}Null`;
    default:
      return rootName;
  }
}

/**
 * Detect the type of a value
 * @param value Value to detect
 * @returns JsonValueType of the value
 */
export function detectType(value: unknown): JsonValueType {
  if (value === null) return JsonValueType.NULL;

  const type = typeof value;

  if (type === "boolean") return JsonValueType.BOOLEAN;
  if (type === "number") return JsonValueType.NUMBER;
  if (type === "string") return JsonValueType.STRING;

  if (Array.isArray(value)) return JsonValueType.ARRAY;
  if (type === "object") return JsonValueType.OBJECT;

  // Default case, should not reach here
  return JsonValueType.NULL;
}

/**
 * Normalize JSON string
 * @param jsonStr JSON string
 * @returns Normalized JSON string
 */
export function normalizeJsonString(jsonStr: string): string {
  // Remove comments
  const withoutComments = jsonStr.replace(/\/\/.*?$|\/\*[\s\S]*?\*\//gm, "");

  // Handle keys without quotes
  const withQuotedKeys = withoutComments.replace(
    /([{,]\s*)([a-zA-Z0-9_$]+)\s*:/g,
    '$1"$2":'
  );

  // Handle trailing commas
  const withoutTrailingCommas = withQuotedKeys.replace(/,\s*([}\]])/g, "$1");

  // Remove trailing semicolons
  const withoutSemicolon = withoutTrailingCommas.replace(/;\s*$/, "");

  return withoutSemicolon;
}

/**
 * Safely parse JSON string
 * @param jsonStr JSON string
 * @returns Parsed JavaScript object
 */
export function safeParseJson(jsonStr: string): unknown {
  try {
    const normalizedJson = normalizeJsonString(jsonStr);
    return JSON.parse(normalizedJson);
  } catch (error) {
    throw new Error(`Unable to parse JSON: ${(error as Error).message}`);
  }
}

/**
 * Generate indentation spaces
 * @param level Indentation level
 * @param spaces Number of spaces per level
 * @returns Indentation string
 */
export function indent(level: number, spaces: number = 2): string {
  return " ".repeat(level * spaces);
}

/**
 * Check if a string is a valid JavaScript identifier
 * @param str String to check
 * @returns Whether it's a valid identifier
 */
export function isValidIdentifier(str: string): boolean {
  // JavaScript identifier rules:
  // 1. First character must be a letter, underscore, or dollar sign
  // 2. Subsequent characters can be letters, digits, underscores, or dollar signs
  // 3. Cannot be a JavaScript keyword
  const keywords = [
    'break', 'case', 'catch', 'class', 'const', 'continue', 'debugger', 'default', 
    'delete', 'do', 'else', 'enum', 'export', 'extends', 'false', 'finally', 'for', 
    'function', 'if', 'implements', 'import', 'in', 'instanceof', 'interface', 'let', 
    'new', 'null', 'package', 'private', 'protected', 'public', 'return', 'static', 
    'super', 'switch', 'this', 'throw', 'true', 'try', 'typeof', 'var', 'void', 
    'while', 'with', 'yield'
  ];
  
  // Check if it's a keyword
  if (keywords.includes(str)) {
    return false;
  }
  
  // Check if it matches identifier rules
  return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(str);
}
